import datetime

import numpy as np

from service.strategyService.BaseStrategyServiceModel import BaseStrategyServiceModel
from service import dataService
import pandas as pd
from utils import utils
class IntradayMovementStrategy(BaseStrategyServiceModel):
    def __init__(self, code, period, window, rule=0):
        """

        :param trade_days: 交易日期列表
        :param period: 周期
        :param rule: 交易规则 0 为 t+0 1 为 t+1 默认为0
        """
        super().__init__('日内动量策略')
        self.__date = datetime.date(2025, 1, 1)
        self.period = period+'min'
        self.window = window  if window is not None else 14
        self.__buy_date = datetime.date(2025, 1, 1)
        self.code = code
        self.rule = rule
        self.current_position = 0
        self.last_position = 0
        self.UpperBound_df = pd.DataFrame()
        self.LowerBound_df = pd.DataFrame()
        self.history_df = pd.DataFrame(columns=['time', 'news', 'high', 'low', 'volume', 'vmap','signal'])

    async def analyze(self, data):
        """
        :param data: 历史分时数据 以date为index 今天与之前的数据
        :return: 返回噪声区的上下边界 (dict, dict), (series, series)
        """
        # 今日的数据
        today_data = data[data['date'] == data.iloc[-1]['date']]
        # 今日开盘价
        today_open_price = today_data['open'].values[0]
        # 过滤掉不是今天的数据
        data = data[data['date'] != data.iloc[-1]['date']]
        # 昨日收盘价 data最后一行就是昨天的收盘价
        yesterday_close_price = data['close'].values[-1]

        # 1. 累计交易日数据 move = | close分时 / open -1 |
        grouped = data.groupby('date')
        group_ls = []

        for date, group in grouped:
            open_price = group['open'].values[0]
            group['move'] = (group['close'] / float(open_price) - 1.0).abs()
            group_ls.append(group)

        data = pd.concat(group_ls)
        # 计算各个时段的平均值
        # 使用 pivot_table 保留二维结构，便于后续 rolling mean
        move_pivot = data.pivot_table(index='date', columns='time', values='move')
        move_df = move_pivot.tail(self.window).dropna(axis=1).mean()

        # 用 numpy 逐元素 max/min 构造边界
        UpperBound_df = pd.Series(np.maximum(today_open_price, yesterday_close_price), index=move_df.index) * (
                    1 + move_df)
        LowerBound_df = pd.Series(np.minimum(today_open_price, yesterday_close_price), index=move_df.index) * (
                    1 - move_df)


        # 存储结果
        self.UpperBound_df = UpperBound_df
        self.LowerBound_df = LowerBound_df

        return (UpperBound_df.to_dict(), LowerBound_df.to_dict()), (UpperBound_df, LowerBound_df)

    async def apply(self, data):
        """
        用于生成交易信号
        :param data: series 日期 ['date']为index 包含单前股票的最新价格 ['news']  和 时间(['time'] (09:45:00))
        :return:
        """
        # 根据当前的data获得当前分析的日期
        date = data['date']
        if date != self.__date:
            print(f'正在分析: {date.strftime("%Y/%m/%d")}的数据.......')
            # 获取数据 更新日期
            self.__date = date

            # 获得时间窗口内的交易日
            trade_days = await dataService.get_tradeDateList(window=self.window, date=date)
            start_date = trade_days[0]
            end_date = trade_days[-1]

            # 获取时间窗口内的分时数据
            _, df = await dataService.get_stockHistList(
                code=self.code,
                start_date=start_date,
                end_date=end_date,
                period=self.period
            )

            if df is None:
                return None
            else:
                await self.analyze(df)


        # 不在交易日内
        if data['time'] not in self.UpperBound_df.index:
            return None

        # 计算vmap
        HLC = (data['high'] + data['low'] + data['news']) / 3.0
        HLC_volume_sum = HLC * data['volume']
        volume_sum = data['volume']
        # 遍历历史data
        for _, row in self.history_df.iterrows():
            HLC = (row['high'] + row['low'] + row['news']) / 3.0
            HLC_volume_sum += HLC * row['volume']
            volume_sum += row['volume']

        data['vmap'] = HLC_volume_sum / volume_sum

        # 信号处理 买点就是比较突破上边界 卖点需要比较突破vmap或者下边界
        signal = 0
        if data['news'] > self.UpperBound_df[data['time']]:
            signal = 1
            self.__buy_date = date
            self.last_position = signal
        # 上一个信号是买 并且满足条件
        if (self.last_position == 1) and (data['news'] < max(self.UpperBound_df[data['time']], data['vmap'])): # 再加一个条件 当前的位置不是0
            if self.rule == 1:
                if  date == self.__buy_date:
                    # t+1 卖出不能和买入在同一天
                    signal = 0
                else:
                    signal = -1
                self.last_position = signal
            else:
                signal = -1
                self.last_position = signal

        # --- 去重复信号：持仓方向控制 ---
        # 如果当前是多头，再来一个买信号就取消
        if signal == 1 and self.current_position == 1:
            signal = 0
        # 如果当前是空头，再来一个卖信号就取消
        if signal == -1 and self.current_position == -1:
            signal = 0
        # 如果换方向，则更新持仓状态
        if signal != 0:
            self.current_position = signal

        data['signal'] = signal
        # 存储历史记录
        self.history_df.loc[len(self.history_df)] = data
        return data
